#include "screen.h"
#include "port.h"
#include "../kernel/util.h"

/* Declaration of private functions */
int get_offset(int col, int row);
int get_cursor_offset();
void set_cursor_offset(int offset);
int get_offset_row(int offset);
int get_offset_col(int offset);
int put_char(char c, int col, int row, char attr);

void clear_screen();

void kprint_at(char *message, int col, int row) {
    int offset;
    if (col >= 0 && row >= 0){
        offset = get_offset(col, row);
    } else {
        offset = get_cursor_offset();
        row = get_offset_row(offset);
        col = get_offset_col(offset);
    }
    /* Loop through message and print it*/
    int i = 0;
    while (message[i] != 0){
        offset = put_char(message[i++], col, row, WHITE_ON_BLACK);
        /* Compute row/col for next iteration */
        row = get_offset_row(offset);
        col = get_offset_col(offset);
    }
}
void kprint(char *message) {
    kprint_at(message, -1, -1);
}

void clear_screen() {
    unsigned char *screen = (unsigned char*) VIDEO_ADDRESS;
    int screen_size = MAX_ROWS * MAX_COLS;
    for (int i = 0; i < screen_size; i++) {
        screen[i * 2] =' ';
        screen[i * 2 + 1] = WHITE_ON_BLACK;
    }
    set_cursor_offset(get_offset(0, 0));
}

int get_offset(int col, int row) {
    return 2 * (MAX_COLS * row + col);
}

int get_cursor_offset() {
    port_byte_out(REG_SCREEN_CTRL, 14);
    int offset = port_byte_in(REG_SCREEN_DATA);
    offset = offset << 8;
    port_byte_out(REG_SCREEN_CTRL, 15);
    offset = offset + port_byte_in(REG_SCREEN_DATA);
    return offset * 2; // 每个字符2个字节
}

void set_cursor_offset(int offset) {
        /* Similar to get_cursor_offset, but instead of reading we write data */
    offset /= 2;
    port_byte_out(REG_SCREEN_CTRL, 14);
    port_byte_out(REG_SCREEN_DATA, (unsigned char) (offset >> 8));
    port_byte_out(REG_SCREEN_CTRL, 15);
    port_byte_out(REG_SCREEN_DATA, (unsigned char) (offset & 0xFF));
}

int get_offset_row(int offset) {
    return offset / 2 / MAX_COLS;
}
int get_offset_col(int offset) {
    return offset / 2 % MAX_COLS;
}

int put_char(char c, int col, int row, char attr) {
    unsigned char *video_meory = (unsigned char*) VIDEO_ADDRESS;
    if (!attr) {
        attr = WHITE_ON_BLACK;
    }
    
    if (col >= MAX_COLS || row >= MAX_ROWS) {
        video_meory[2 * MAX_COLS * MAX_ROWS -2] = 'E';
        video_meory[2 * MAX_COLS * MAX_ROWS - 1] = RED_ON_WHITE;
        return get_offset(col, row);
    }

    int offset;
    if (col >= 0 && row >= 0) offset = get_offset(col, row);
    else offset = get_cursor_offset();

    if (c == '\n'){
       row = get_offset_row(offset);
       offset = get_offset(0, row + 1);
    } else {
        video_meory[offset] = c;
        video_meory[offset + 1] = attr;
        offset += 2;
    }

    // check if the offset over screen size and scroll */
    if (offset >= 2 * MAX_COLS * MAX_ROWS){
        for (int i = 1; i < MAX_ROWS; i++){
            memory_copy(get_offset(0, i) + video_meory, get_offset(0, i - 1) + video_meory, MAX_COLS * 2);
        }
        // clear last row
        offset = get_offset(0, MAX_ROWS - 1);
        for (int i = 0; i < MAX_COLS; i++)
        {
            video_meory[offset + i * 2] = ' ';
        }
    }

    set_cursor_offset(offset);
    return offset;
}